<!--
 * @Author: JiraiyaChen 979526234@qq.com
 * @Date: 2025-02-12 13:47:51
 * @LastEditors: JiraiyaChen 979526234@qq.com
 * @LastEditTime: 2025-08-06 10:16:05
 * @Description: 封装form表单组件
 * @usage: <ClForm ref="formRef" :form-item="formItem" />
 * @props 接受： {Array} formItem 表单数据
 * @export {Object} onsubmit 提交表单
-->

<template>
  <div class="my-form">
    <!-- <el-card>表单数据：{{ formData }}</el-card> -->
    <el-form
      ref="refElForm"
      :model="formData"
      v-bind="$attrs"
      :scroll-to-error="true"
      label-position="right"
      :label-width="labelWidth"
      @submit.prevent
    >
      <el-form-item
        v-for="(item, index) in formItem"
        :key="item.key"
        :style="{ width: item?.elementConfig?.textareaWidth, display: item.formType == 'empty' ? 'none' : '' }"
        :label="item.name ? item.name + ':' : ''"
        :prop="item.key"
        :class="item?.elementConfig?.class"
        :rules="getRules(item.verificationData)"
      >
        <div v-if="item.formType == 'info'">: {{ item.value }}</div>
        <div v-if="item.formType == 'component'">
          <component :is="item.elementConfig.component" :item="item" />
        </div>
        <div v-if="item.formType == 'empty'" style="display: none" />
        <!-- 人员多选框 value 类型为数组 -->
        <div v-if="item.formType == 'dialog-multiple'" class="flex items-center gap-3">
          <div class="flex gap-2 flex-wrap">
            <el-tag v-for="(obj, index) in item.value" :key="obj.name" @close="onCloseTag(item.value, index)">
              {{ obj.name || obj.userName }}
            </el-tag>
          </div>
          <el-button :disabled="item.elementConfig.disabled" link type="primary" @click="openDialog(item, index)">
            <span>请选择</span>
          </el-button>
        </div>
        <div v-if="item.formType == 'dialog'">
          <!-- {{ item.value }} -->
          <el-button link type="primary" :disabled="item.elementConfig.disabled" @click="openDialog(item, index)">
            <span v-if="item.value?.name">{{ item.value?.name }}</span>
            <span v-else>{{ '请选择' }}</span>
          </el-button>
          <span :style="{ pointerEvents: item.elementConfig.disabled ? 'none' : 'auto' }">
            <svg-icon v-if="item.value?.name" icon-class="删除" class="on-del" @click="onDelSelect(item)" />
          </span>
        </div>
        <el-tree
          v-if="item.formType == 'el-tree'"
          v-model="item.value"
          style="width: 100%"
          :data="item.elementConfig.data"
          node-key="name"
          multiple
          :render-after-expand="false"
          show-checkbox
          :props="{
            children: 'children',
            label: 'name'
          }"
          :default-checked-keys="item.elementConfig.defaultCheckedKeys"
        />
        <el-cascader
          v-if="item.formType == 'el-cascader'"
          v-model="item.value"
          style="width: 100%"
          :options="item.elementConfig.options"
          :props="item.elementConfig.props"
          :placeholder="getPlaceholder(item)"
          filterable
          clearable
        />

        <el-cascader-panel
          v-if="item.formType == 'el-cascader-panel'"
          v-model="item.value"
          :options="item.elementConfig.options"
          :props="item.elementConfig.props"
        />

        <el-input-number
          v-if="item.formType == 'input-number'"
          v-model="item.value"
          style="width: 100%"
          clearable
          :placeholder="getPlaceholder(item)"
          :controls-position="item.elementConfig.position || 'right'"
          :min="item.elementConfig.min"
          :max="item.elementConfig.max"
          :step="item.elementConfig.step"
        />
        <el-input
          v-if="item.formType == 'input-val-number'"
          v-model.number="item.value"
          clearable
          :placeholder="getPlaceholder(item)"
        >
          <template v-if="item.elementConfig.suffix" #suffix>
            {{ item.elementConfig.suffix }}
          </template>
        </el-input>
        <div v-if="item.formType == 'many-input'" class="many-input-box">
          <!-- {{ item.value }} -->
          <el-input
            v-for="(val, index) in item.elementConfig.manyInputLength"
            :key="index"
            v-model.trim="item.value[index]"
            class="mb-1"
            clearable
          >
            <template #append>
              <el-button v-if="index == 0" circle :icon="Plus" @click="onAddInput(item)" />
              <el-button v-if="index !== 0" circle :icon="Minus" @click="onDelInput(item, index)" />
            </template>
          </el-input>
        </div>
        <el-input
          v-if="item.formType == 'textarea'"
          v-model="item.value"
          :rows="item.elementConfig.rows"
          :disabled="item.elementConfig.disabled"
          type="textarea"
          :placeholder="getPlaceholder(item)"
        />
        <el-input
          v-if="item.formType == 'input'"
          v-model.trim="item.value"
          clearable
          :placeholder="item.elementConfig.placeholder ?? getPlaceholder(item)"
          :type="item.elementConfig.type ?? 'text'"
          :rows="item.elementConfig.rows"
          :disabled="item.elementConfig.disabled"
          @change="onChangeInput(item)"
        >
          <template v-if="item.elementConfig.prepend" #prepend>
            {{ item.elementConfig.prepend }}
          </template>
          <template v-if="item.elementConfig.button" #append>
            <el-button type="primary" @click="onPrepend(item)">
              {{ item.elementConfig.button }}
            </el-button>
          </template>
          <template v-if="item.elementConfig.suffix" #suffix>
            {{ item.elementConfig.suffix }}
          </template>
        </el-input>
        <el-select
          v-if="item.formType == 'select'"
          v-model="item.value"
          clearable
          filterable
          collapse-tags
          popper-class="select-custom-header"
          :max-collapse-tags="1"
          style="width: 100%"
          :placeholder="getPlaceholder(item)"
          :disabled="item.elementConfig.disabled"
          :multiple="item.elementConfig.multiple"
          @change="optionChange(item)"
        >
          <slot v-if="item.elementConfig.multiple" name="select-header" />
          <el-option
            v-for="childrenItem in item.elementConfig.options"
            :key="childrenItem.key"
            :label="childrenItem.value"
            :value="childrenItem.key"
          />
        </el-select>
        <el-date-picker
          v-if="item.formType === 'year'"
          v-model="item.value"
          style="width: 100%"
          value-format="YYYY"
          clearable
          type="year"
          :placeholder="getPlaceholder(item)"
        />
        <el-date-picker
          v-if="item.formType == 'month'"
          v-model="item.value"
          style="width: 100%"
          type="month"
          :format="item.elementConfig?.valueFormat || 'YYYY-MM'"
          :value-format="item.elementConfig?.valueFormat || 'YYYY-MM'"
          :placeholder="getPlaceholder(item)"
          clearable
        />
        <el-date-picker
          v-if="item.formType == 'date'"
          v-model="item.value"
          style="width: 100%"
          type="date"
          value-format="YYYY-MM-DD"
          :placeholder="getPlaceholder(item)"
          :disabled="item.elementConfig.disabled"
          clearable
        />
        <el-date-picker
          v-if="item.formType == 'date-range'"
          v-model="item.value"
          style="width: 100%"
          type="daterange"
          value-format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="起始"
          end-placeholder="结束"
          :placeholder="getPlaceholder(item)"
          :disabled="item.elementConfig.disabled"
          clearable
        />
        <el-date-picker
          v-if="item.formType == 'date-datetime'"
          v-model="item.value"
          style="width: 100%"
          type="datetime"
          :disabled="item.elementConfig.disabled"
          :placeholder="getPlaceholder(item)"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
        <el-date-picker
          v-if="item.formType == 'date-datetimerange'"
          v-model="item.value"
          style="width: 100%"
          :disabled="item.elementConfig.disabled"
          type="datetimerange"
          start-placeholder="起始时间"
          end-placeholder="结束时间"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
        <el-radio-group v-if="item.formType == 'radio'" v-model="item.value">
          <el-radio
            v-for="childrenItem in item.elementConfig.options"
            :key="childrenItem.label"
            :label="childrenItem.value"
          >
            {{ childrenItem.label }}
          </el-radio>
        </el-radio-group>
        <el-upload
          v-if="item.formType == 'avatar'"
          class="avatar-uploader"
          :action="action"
          :headers="headers"
          name="uploadFile"
          :show-file-list="false"
          :on-success="
            (res, file) => {
              handleAvatarSuccess(res, file, item)
            }
          "
          :before-upload="beforeAvatarUpload"
          accept=".png,.jpg,.jpeg"
        >
          <img v-if="item.elementConfig.avatarUrl" :src="item.elementConfig.avatarUrl" class="avatar" />
          <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
        </el-upload>

        <el-upload
          v-if="item.formType == 'picture-card'"
          v-model:file-list="item.value"
          :action="action"
          :headers="headers"
          name="uploadFile"
          list-type="picture-card"
          :on-preview="handlePictureCardPreview"
        >
          <el-icon><Plus /></el-icon>
        </el-upload>

        <el-dialog v-model="dialogVisible">
          <img w-full :src="dialogImageUrl" alt="Preview Image" />
        </el-dialog>
      </el-form-item>
      <el-form-item>
        <slot name="opt-btn" />
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import { Minus, Plus } from '@element-plus/icons-vue'
import { formRules } from './data'
import type { PropType } from 'vue'

const props = defineProps({
  formItem: {
    required: true,
    type: Array as PropType<any[]>
  },
  labelWidth: {
    type: String,
    default: '180px'
  }
})
const onAddInput = (item) => {
  item.elementConfig.manyInputLength++
  item.value.push('')
}
const onDelInput = (item, index) => {
  item.elementConfig.manyInputLength--
  item.value.splice(index, 1)
}
const onCloseTag = (list, index) => {
  list.splice(index, 1)
}
const onDelSelect = (item) => {
  item.value = null
  $emit('clear-dialog-val', item)
}
const openDialog = (item, index) => {
  $emit('open-dialog', item, index)
}
// 双向绑定数据
let $emit = defineEmits([
  'on-change-select',
  'update:formItem',
  'on-prepend',
  'open-dialog',
  'clear-dialog-val',
  'on-change-radio',
  'onChangeInput'
])
const onChangeInput = (item) => {
  $emit('onChangeInput', item)
}
// patch 后缀点击打开弹框
const onPrepend = (item) => {
  $emit('on-prepend', item)
}
const refElForm = ref<any>(null)
const onsubmit = async (callBack) => {
  try {
    const flag = await refElForm.value.validate()
    if (flag) {
      callBack(formData.value)
    }
  } catch (error) {
    console.error('表单数据不通过👉👉👉', error)
  }
}
const formData = computed(() => {
  const obj = {}
  props.formItem.forEach((element) => {
    obj[element.key] = element.value
  })
  return obj
})

const maxScoreValid = (rule, value, callback) => {
  const regex = /^(\d+(\.\d+)?)(\/\d+(\.\d+)?)*$/
  const tempMaxScore = formData.value['maxScore']
  if (regex.test(tempMaxScore)) {
    callback()
  } else {
    callback(new Error(`请填写正确的值`))
  }
}
// 最大值 数字， 并且大于 最小值
const minScoreValid = (rule, value, callback) => {
  const regex = /^(\d+(\.\d+)?)(\/\d+(\.\d+)?)*$/
  const tempMaxScore = formData.value['maxScore']
  const tempMinScore = formData.value['minScore']

  if (!regex.test(tempMaxScore)) {
    callback(new Error(`最高分必须大于最低分`))
  }
  if (!regex.test(tempMinScore)) {
    callback(new Error(`最高分必须大于最低分`))
  }

  const flag = Number(tempMaxScore) > Number(tempMinScore)
  if (flag) {
    callback()
  } else {
    callback(new Error(`最高分必须大于最低分`))
  }
}

// 获取 简易 认证
const getRules = (item) => {
  if (item === '最高分') {
    return [{ required: true, validator: maxScoreValid, trigger: 'blur' }]
  }
  if (item === '最低分') {
    return [{ required: true, validator: minScoreValid, trigger: 'blur' }]
  }
  if (typeof item === 'string') {
    return formRules[item]
  }
  return item
}
// 默认 文本
const getPlaceholder = (item) => {
  if (item.name === '至' || item.name === '面积') {
    return ''
  }
  if (item.formType == 'input' || item.formType == 'input-area' || item.formType == 'input-number') {
    return `请输入${item.name}`
  } else {
    return `请选择${item.name}`
  }
}
// 下拉框改变 获取所有信息
const optionChange = (item) => {
  $emit('on-change-select', item)
}

// 用户头像
const handleAvatarSuccess = (response, uploadFile, item) => {
  item.value = response.data
  item.elementConfig.avatarUrl = URL.createObjectURL(uploadFile.raw!)
}

const beforeAvatarUpload = (rawFile) => {
  if (!['image/png', 'image/jpeg'].includes(rawFile.type)) {
    elMessage('只能上传JPG/PNG格式', 'error')
    return false
  } else if (rawFile.size / 1024 / 1024 > 2) {
    elMessage('不能大于2MB', 'error')
    return false
  }
  return true
}
// 照片墙
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const handlePictureCardPreview = (uploadFile) => {
  dialogImageUrl.value = uploadFile.url!
  dialogVisible.value = true
}
// 图片上传地址
const action = `${import.meta.env.VITE_APP_BASE_URL}/api/v1/fileList/upload`
const _useBasicStore = useBasicStore()
const headers = ref({
  Authorization: _useBasicStore.token
})

const resetForm = () => {
  // refElForm.value.resetFields()
  props.formItem.forEach((element) => {
    element.value = null
  })
}

// 下拉全选补丁
const scopeListItem = computed(
  () =>
    props.formItem.find((item) => item.formType == 'scopeList') ?? {
      value: [],
      elementConfig: {
        options: []
      }
    }
)

defineExpose({ onsubmit, formData, refElForm, resetForm })
</script>

<style scoped lang="scss">
.on-del {
  cursor: pointer;
  margin-left: 3px;
}
:deep(.el-form-item__label) {
  font-weight: 400;
  font-size: 15px;
  // color: #9e9e9e;
  line-height: 40px;
  margin-right: 10px;
}
.my-form {
  width: 100%;
}
:deep(.el-form-item) {
  align-items: center;
}
:deep(.el-form) {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 10px;
}
:deep(.empty) {
  display: none;
}

:deep(.many-input-box) {
  width: 100%;
  .el-input-group__append {
    margin-left: 10px;
    box-shadow: unset;
    padding: 0;
  }
}
</style>
<style scoped>
.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>
